/*
 * Copyright (C) 2021 HiHope Open Source Organization .
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 *
 * limitations under the License.
 */
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include "ohos_init.h"
#include "cmsis_os2.h"
#include "wifi_hotspot.h"
#include "lwip/netifapi.h"

#include "ssd1306.h"

static osThreadId_t g_ApTaskId = NULL;
static int g_ApExit = 0;

static volatile int g_hotspotStarted = 0;
static void OnHotspotStateChanged(int state)
{
    printf("OnHotspotStateChanged: %d.\r\n", state);
    if (state == WIFI_HOTSPOT_ACTIVE) {
        g_hotspotStarted = 1;
    } else {
        g_hotspotStarted = 0;
    }
}

static volatile int g_joinedStations = 0;
void DispayApInfo(void)
{
    ssd1306_Fill(Black);
    ssd1306_SetCursor(5, 5);
    ssd1306_DrawString("WIFI AP", Font_11x18, White);
    ssd1306_SetCursor(5, 5+18+5);
    if (g_joinedStations) {
        ssd1306_DrawString("LINK...... OK", Font_7x10, White);
    } else {
        ssd1306_DrawString("LINK......", Font_7x10, White);
    }
    ssd1306_SetCursor(5, 5+18+5+10+5+10);
    ssd1306_DrawString("UP          DOWN", Font_7x10, White);
    ssd1306_UpdateScreen();
}

static void PrintStationInfo(StationInfo* info)
{
    if (!info) return;
    static char macAddress[32] = {0};
    unsigned char* mac = info->macAddress;
    snprintf(macAddress, sizeof(macAddress), "%02X:%02X:%02X:%02X:%02X:%02X",
        mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    printf(" PrintStationInfo: mac=%s, reason=%d.\r\n", macAddress, info->disconnectedReason);
}

static void OnHotspotStaJoin(StationInfo* info)
{
    g_joinedStations++;
    PrintStationInfo(info);
    printf("+OnHotspotStaJoin: active stations = %d.\r\n", g_joinedStations);
    DispayApInfo();
}

static void OnHotspotStaLeave(StationInfo* info)
{
    g_joinedStations--;
    PrintStationInfo(info);
    printf("-OnHotspotStaLeave: active stations = %d.\r\n", g_joinedStations);
    DispayApInfo();
}

WifiEvent g_defaultWifiEventListener = {
    .OnHotspotStaJoin = OnHotspotStaJoin,
    .OnHotspotStaLeave = OnHotspotStaLeave,
    .OnHotspotStateChanged = OnHotspotStateChanged,
};

static struct netif* g_iface = NULL;
int StartHotspot(const HotspotConfig* config)
{
    WifiErrorCode errCode = WIFI_SUCCESS;

    errCode = RegisterWifiEvent(&g_defaultWifiEventListener);
    printf("RegisterWifiEvent: ret=%d\r\n", errCode);

    errCode = SetHotspotConfig(config);
    printf("SetHotspotConfig: ret=%d\r\n", errCode);

    g_hotspotStarted = 0;
    printf("EnableHotspot:\r\n");
    errCode = EnableHotspot();
    printf("EnableHotspot: ret=%d\r\n", errCode);

    while (!g_hotspotStarted) {
        osDelay(10);
    }
    printf("g_hotspotStarted = %d.\r\n", g_hotspotStarted);

    g_iface = netifapi_netif_find("ap0");
    if (g_iface) {
        ip4_addr_t ipaddr;
        ip4_addr_t gateway;
        ip4_addr_t netmask;

        IP4_ADDR(&ipaddr,  192, 168, 1, 1);     /* input your IP for example: 192.168.1.1 */
        IP4_ADDR(&gateway, 192, 168, 1, 1);     /* input your gateway for example: 192.168.1.1 */
        IP4_ADDR(&netmask, 255, 255, 255, 0);   /* input your netmask for example: 255.255.255.0 */
        err_t ret = netifapi_netif_set_addr(g_iface, &ipaddr, &netmask, &gateway);
        printf("netifapi_netif_set_addr: ret=%d\r\n", ret);

        ret = netifapi_dhcps_stop(g_iface);
        printf("netifapi_dhcps_stop: ret=%d\r\n", ret);

        ret = netifapi_dhcps_start(g_iface, 0, 0);
        printf("netifapi_dhcp_start: ret=%d\r\n", ret);
    }
    return errCode;
}

void StopHotspot(void)
{
    if (g_iface) {
        err_t ret = netifapi_dhcps_stop(g_iface);
        printf("netifapi_dhcps_stop: ret=%d\r\n", ret);
    }

    WifiErrorCode errCode = UnRegisterWifiEvent(&g_defaultWifiEventListener);
    printf("UnRegisterWifiEvent: ret=%d\r\n", errCode);

    errCode = DisableHotspot();
    printf("EnableHotspot: ret=%d\r\n", errCode);

    g_iface = NULL;
    g_joinedStations = 0;
}

void WifiHotspotTask(void)
{
    WifiErrorCode errCode;
    HotspotConfig config = {0};

    strcpy(config.ssid, "DSX--AP");
    strcpy(config.preSharedKey, "88888888");
    config.securityType = WIFI_SEC_TYPE_PSK;
    config.band = HOTSPOT_BAND_TYPE_2G;
    config.channelNum = 7;

    printf("[WifiHotspotTask] StartHotspot: %s ...\n", config.ssid);
    errCode = StartHotspot(&config);
    printf("[WifiHotspotTask] StartHotspot: ret=%d\n", errCode);

    DispayApInfo();

    int timeout = 60;
    while (timeout-- && !g_ApExit) {
        printf("[WifiHotspotTask] after %d seconds Ap will turn off!\n", timeout);
        osDelay(100);
    }

    StopHotspot();
    g_ApExit = 0;
    g_ApTaskId = NULL;
    printf("[WifiHotspotTask] StopHotspot: %s ...\n", config.ssid);
}

void WifiApTestEntry(void)
{
    if (g_ApTaskId) {
        return;
    }

    //osPriorityAboveNormal[32], osPriorityNormal[24]
    //{.name, .attr_bits, .cb_mem, .cb_size, .stack_mem, .stack_size, .priority, .tz_module, .reserved}
    osThreadAttr_t attr = {"WifiHotspotTask", 0, NULL, 0, NULL, 1024*8, 24, 0, 0};
    g_ApTaskId = osThreadNew((osThreadFunc_t)WifiHotspotTask, NULL, &attr);
    if (g_ApTaskId == NULL) {
        printf("[WifiApTestEntry] Falied to create %s!\n", attr.name);
    }
}

void WifiApTestExit(void)
{
    g_ApExit = 1;
}